#include "base/logging.h"
#include "dispatch_message_task.h"

#include "base/html_escape.h"
#include "base/url_encode.h"
#include "base/curl_client.h"

#include "im/common/common.h"

#include "im/adapters/user_profile_adapter.h"
#include "im/adapters/session_adapter.h"
#include "im/adapters/message_outlet_adapter.h"
#include "im/adapters/message_history_adapter.h"
#include "im/adapters/recent_contact_adapter.h"

#include "shop_info_cache.h"
#include "notify_filter.h"

namespace imserver {

void TimerHandler(const boost::system::error_code& error, int64_t shop_client, int64_t shop_keeper);

class ShopTimerMap {
 public:
  static ShopTimerMap & Instance() {
    static ShopTimerMap instance; 
    return instance;
  }

  void StartTimer(int64_t shop_keeper, int64_t shop_client) {
    {
      boost::mutex::scoped_lock lock(mutex_);
      TimerMapType::iterator shop_it = shop_timers_.find(shop_keeper);
      if (shop_it != shop_timers_.end()) {
        std::map<uint64_t, boost::asio::deadline_timer*>::iterator timer_it = shop_it->second.find(shop_client);
        if (timer_it != shop_it->second.end()) {
          return;
        }
      }
    }

    boost::asio::deadline_timer * timer = new boost::asio::deadline_timer(io_service_, boost::posix_time::seconds(60));
    timer->async_wait(boost::bind(&TimerHandler, boost::asio::placeholders::error, shop_client, shop_keeper));
    LOG_DEBUG("start timer, sender=" << shop_client << " receiver=" << shop_keeper);

    {
      boost::mutex::scoped_lock lock(mutex_);
      shop_timers_[shop_keeper].insert(std::make_pair(shop_client, timer));
    }
  }

  void ClearTimer(int64_t shop_keeper, int64_t shop_client) {
    DoClearTimer(shop_keeper, shop_client, false);
  }

  void CancelTimer(int64_t shop_keeper, int64_t shop_client) {
    DoClearTimer(shop_keeper, shop_client, true);
  }

  void Start() {
    pthread_t thread;
    pthread_create(&thread, NULL, WorkerThread, &io_service_);
    pthread_detach(thread);
  }

  static void * WorkerThread(void * data) {
    LOG_WARN("io service WorkerThread start");
  
    boost::asio::io_service * io_service = reinterpret_cast<boost::asio::io_service *>(data);
    for (;;) {
      boost::asio::deadline_timer timer(*io_service, boost::posix_time::hours(1000000));
      // timer->expires_from_now(boost::posix_time::seconds(10));
      timer.async_wait(boost::bind(&TimerHandler, boost::asio::placeholders::error, 0, 0));
  
      try {
        io_service->run();
        LOG_WARN("io_service stoped.");
      } catch (std::exception& e) {
        LOG_FATAL("io_service exception:" << e.what());
      }
    }
    LOG_WARN("io service WorkerThread exit");
    return NULL;
  }


 private:
  void DoClearTimer(int64_t shop_keeper, int64_t shop_client, bool need_cancel) {
    boost::asio::deadline_timer * timer = NULL;
    {
      boost::mutex::scoped_lock lock(mutex_);
      TimerMapType::iterator shop_it = shop_timers_.find(shop_keeper);
      if (shop_it == shop_timers_.end()) {
        return;
      }
      std::map<uint64_t, boost::asio::deadline_timer*>::iterator timer_it = shop_it->second.find(shop_client);
      if (timer_it == shop_it->second.end()) {
        return;
      }
      LOG_INFO("timer canceled, shop_keeper=" << shop_keeper << " shop_client=" << shop_client);
      timer = timer_it->second;
      
      shop_it->second.erase(timer_it);
    }
    if (need_cancel) {
      timer->cancel();
    }
    delete timer;
  }

  typedef std::map<uint64_t, std::map<uint64_t, boost::asio::deadline_timer*> > TimerMapType;
  TimerMapType shop_timers_;

  boost::asio::io_service io_service_;
  boost::mutex mutex_;
};

void TimerHandler(const boost::system::error_code& error, int64_t shop_client, int64_t shop_keeper) {
  if (error) {
    LOG_INFO("TimerHandler canceled, shop_client=" << shop_client << " shop_keeper=" << shop_keeper);
    return;
  }

  ShopTimerMap::Instance().ClearTimer(shop_keeper, shop_client);

  LOG_INFO("TimerHandler triggered, shop_client=" << shop_client << " shop_keeper=" << shop_keeper);
  SessionList sender_sessions;
  SessionAdapter::Instance().GetSessionList(shop_client, &sender_sessions, 2);

  ImMessage notify_message;
  notify_message.sender = shop_keeper;
  notify_message.receiver = shop_client;
  notify_message.timestamp = time(NULL);

  SetMessageType(notify_message, MSG_TYPE_OFFLINE_NOTIFY);
  std::stringstream body;

  ShopInfo shop_info = ShopInfoCache::Instance().Get(shop_keeper);
  body << "【360同城帮】商家\"" << GetUsername(shop_keeper) << "(电话" << shop_info.phone << ")\"现在忙，"
       << "系统为您找到了其他在线商家，<a class=\"broadcast-client\" data-shop=\""
       << shop_keeper << "\" href=\"#nogo\">点击这里</a>向他们求助。";
  notify_message.body = body.str();

  for(size_t i = 0; i < sender_sessions.size(); ++i) {
    int res = MessageOutletAdapter::Instance().DistributeMessage(sender_sessions[i].key, notify_message, 2);
    LOG_INFO("notify message type=" << MessageType(notify_message) << " fake_sender=" << shop_keeper << " reveiver=" 
             << notify_message.receiver << " res=" << res);
  }
}


void DispatchMessageTask::StartIoService() {
  ShopTimerMap::Instance().Start();
}

void DispatchMessageTask::FromShopMessage(const ImMessage& message) {
  DispatchDefaultMessage(const_cast<ImMessage&>(message));
  LOG_INFO("FromShopMessage, shop_keeper=" << message.sender << " shop_client=" << message.receiver);
  ShopTimerMap::Instance().CancelTimer(message.sender, message.receiver);
}

void DispatchMessageTask::ToShopMessage(const ImMessage& message) {
  LOG_INFO("ToShopMessage " << message.seq_id << " sender=" << message.sender << " receiver=" << message.receiver);

  SessionList recv_sessions;
  
  if (0 != SessionAdapter::Instance().GetSessionList(message.receiver, &recv_sessions, 2)) {
    LOG_WARN("GetSessionList error, receiver=" << message.receiver);
  }

  if (recv_sessions.empty()) {
    SessionList sender_sessions;
    SessionAdapter::Instance().GetSessionList(message.sender, &sender_sessions, 2);

    ImMessage notify_message;
    notify_message.sender = message.receiver;
    notify_message.receiver = message.sender;
    notify_message.timestamp = time(NULL);

    SetMessageType(notify_message, MSG_TYPE_OFFLINE_NOTIFY);
    std::stringstream body;
    ShopInfo shop_info = ShopInfoCache::Instance().Get(message.receiver);
    body << "【360同城帮】商家暂时不在线，我们已向他发送短信通知。<br/>商家电话：" 
         << shop_info.phone << "<br/>商铺地址：" << shop_info.address + "<br/>"
         << "您也可以<a class=\"broadcast-client\" data-shop=\"" << message.receiver
         << "\" href=\"#nogo\">点击这里</a>向其他商家求助。";
    notify_message.body = body.str();

    for(size_t i = 0; i < sender_sessions.size(); ++i) {
      int res = MessageOutletAdapter::Instance().DistributeMessage(sender_sessions[i].key, notify_message, 2);
      LOG_INFO("notify message type=" << MessageType(notify_message) << message.receiver << " reveiver=" 
               << notify_message.receiver << " res=" << res);
    }

    if (NotifyFilter::Instance().MostRecentTalked(message.sender, message.receiver) == 0) {
      // 通知导购
      {
        static int64_t customer_guide_ids[] = {317126174, 317124696, 317121850, 317125217};
        int64_t selected_guide = customer_guide_ids[rand() % (sizeof(customer_guide_ids) / sizeof(int64_t))];

        SessionList sessions;
        SessionAdapter::Instance().GetSessionList(selected_guide, &sessions, 2);
        LOG_INFO("offline message guide=" << selected_guide << " session_count=" << sessions.size());
        if (!sessions.empty()) {
          ImMessage guide_message;
          guide_message.sender = message.sender;
          guide_message.timestamp = time(NULL);
          SetMessageType(guide_message, MSG_TYPE_OFFLINE_NOTIFY);
          guide_message.body = "【360同城帮】该客户已给商家离线留言，需要导购提供帮助。";
          guide_message.receiver = selected_guide;
          for(size_t j = 0; j < sessions.size(); ++j) {
            MessageOutletAdapter::Instance().DistributeMessage(sessions[j].key, guide_message, 2);
          }
        }
      }

      // 发短信
      std::stringstream post_body;
      std::string rsp;
      post_body << "mobile=" << shop_info.phone << "&content=" 
                << base::UrlEncode("【360同城帮】商家您好：有客户给您的店铺留言了，请及时登录查看。");
      // TODO : 关掉DEBUG
      int res = 0; // base::CurlClient::Instance().Post("http://mobile.helpton.com/mobile_code.php", post_body.str(), &rsp);
      if (res == 0) {
        LOG_INFO("send SMS ok, shop=" << message.receiver << " msg_type=" << MessageType(message) << " post_body=" + post_body.str() + " rsp=" << rsp);
      } else {
        LOG_WARN("send SMS err, shop=" << message.receiver << " post_body=" + post_body.str());
      }
    }
    if (message.seq_id > 0) {
      MessageHistoryAdapter::Instance().AddMessage(message, 2);
    }
    RecentContactAdapter::Instance().Add(message.sender, message.receiver, 2);
  } else {
    if (message.flags & MSG_FLAG_RECREATE_ON_LINGER) {
      // 有其他副本在线，不需要RECREATE_ON_LINGER
      LOG_INFO("receiver " << message.receiver << " other session online, RECREATE_ON_LINGER not required");
      return;
    }

    DispatchDefaultMessage(const_cast<ImMessage&>(message));

    if (NotifyFilter::Instance().MostRecentTalked(message.sender, message.receiver) == 0) {
      ShopTimerMap::Instance().StartTimer(message.receiver, message.sender);
      LOG_DEBUG("start shop timer sender=" << message.sender << " receiver=" << message.receiver);
    }
  }
}

void DispatchMessageTask::run() {
  if (ShopInfoCache::Instance().Contains(message_.receiver)) {
    ToShopMessage(message_);
  } else if (ShopInfoCache::Instance().Contains(message_.sender)) {
    FromShopMessage(message_);
  } else {
    DispatchDefaultMessage(message_);
  }
}

}


